package se.webbzon.oschi01.worldobject.appearance;

import javax.media.j3d.Geometry;
import javax.media.j3d.GeometryArray;
import javax.media.j3d.TriangleStripArray;
import javax.vecmath.Point3d;
import javax.vecmath.TexCoord2f;

import com.sun.j3d.utils.geometry.GeometryInfo;
import com.sun.j3d.utils.geometry.NormalGenerator;
import com.sun.j3d.utils.geometry.Stripifier;

public class EllipsoidAppearance3D extends SimpleAppearance3D {

	// The geometry model of the ellipsoid
	private static final Geometry model = createModel(1,1,1,18,false);
	
	// The geometry model of the back-faced ellipsoid
	private static final Geometry modelBackfaced = createModel(1,1,1,18,true);

	/*============================================================
	Constructors
	============================================================*/
	
	/** Creates a new WorldObjectEllipsoidAppearance. The center of the 
	 * ellipsoid will be placed in the origin. The height, depth and
	 * width will be equal to 1. The number of divisions will be equal 
	 * to 18. **/
	public EllipsoidAppearance3D() {
		this(0,0,false);
	}
	
	/** Creates a new WorldObjectEllipsoidAppearance. The center of the 
	 * ellipsoid will be placed in the origin. The height, depth and
	 * width will be equal to 1. The number of divisions will be equal 
	 * to 18. See WorldObjectSimpleAppearance for full description of 
	 * the attributes renderingOrder and simpleAppearanceProperties. 
	 * The appearance may be back-faced. **/
	public EllipsoidAppearance3D(	int renderingOrder,
									int simpleAppearanceProperties,
									boolean backfaced) {
		super(renderingOrder, simpleAppearanceProperties, backfaced ? modelBackfaced : model);
	}
	
	/** Creates a new WorldObjectEllipsoidAppearance. The center of the 
	 * ellipsoid will be placed in the origin. The height, depth and
	 * width will be equal to 1. The number of divisions is given by
	 * the input. **/
	public EllipsoidAppearance3D(int divisions) {
		this(0,0,divisions,false);
	}
		
	/** Creates a new WorldObjectEllipsoidAppearance. The center of the 
	 * ellipsoid will be placed in the origin. The height, depth and
	 * width will be equal to 1. The number of divisions is given by
	 * the input. See WorldObjectSimpleAppearance for full description 
	 * of the attributes renderingOrder and simpleAppearanceProperties. 
	 * The appearance may be back-faced. **/
	public EllipsoidAppearance3D(	int renderingOrder,
									int simpleAppearanceProperties, 
									int divisions,
									boolean backfaced) {
		super(renderingOrder,simpleAppearanceProperties,createModel(1,1,1,divisions,backfaced));
	}
		
	/*============================================================
	Private Methods
	============================================================*/
		
	/** Creates the geometry model. **/
	private static Geometry createModel(	double height,
											double width,
											double depth,
											int divisions,
											boolean backfaced) {
		final double h = height/2,w = width/2,d = depth/2;
		final int[] stripVertexCount = new int[divisions];
		for (int i = 0; i < divisions; i++) 
			stripVertexCount[i] = 2+2 * (divisions/2);
		final TriangleStripArray ta = new TriangleStripArray(
												2*divisions*(1+divisions/2),
												GeometryArray.COORDINATES +
												GeometryArray.NORMALS + 
												GeometryArray.TEXTURE_COORDINATE_2,
												stripVertexCount);
		final Point3d top = new Point3d(0,h,0);
		final Point3d bottom = new Point3d(0,-h,0);
		final Point3d p = new Point3d();
		final TexCoord2f texCoord = new TexCoord2f();
		
		final double dPhi = backfaced ? 2*Math.PI/divisions : -2*Math.PI/divisions;
		final double dTheta = Math.PI/(divisions/2);
		int vertex = 0;
		for (int i = 0; i < divisions; i++) {
			final double phi = dPhi * i;
			texCoord.set((i+0.5f)/divisions, 1);
			ta.setTextureCoordinate(0, vertex, texCoord);
			ta.setCoordinate(vertex++, top);
			for (int j = 0; j < (divisions)/2; j++) {
				final double theta = dTheta * j;
				final double py = h*Math.cos(theta);
				double px = w*Math.sin(theta)*Math.cos(phi);
				double pz = d*Math.sin(theta)*Math.sin(phi);
				p.set(px,py,pz);
				texCoord.set(i/(float)divisions, 1-j/(float)(divisions/2));
				ta.setTextureCoordinate(0, vertex, texCoord);
				ta.setCoordinate(vertex++, p);
				
				px = w*Math.sin(theta)*Math.cos(phi+dPhi);
				pz = d*Math.sin(theta)*Math.sin(phi+dPhi);
				p.set(px,py,pz);
				texCoord.set((i+1)/(float)divisions, 1-j/(float)(divisions/2));
				ta.setTextureCoordinate(0, vertex, texCoord);
				ta.setCoordinate(vertex++, p);
			}
			texCoord.set((i+0.5f)/divisions, 0);
			ta.setTextureCoordinate(0, vertex, texCoord);
			ta.setCoordinate(vertex++, bottom);
		}
		
		// Generate normals
		final GeometryInfo g = new GeometryInfo(ta);
		final NormalGenerator ng = new NormalGenerator();
		ng.generateNormals(g);
		// Optimize the ellipsoid by using a stripifier (optionally) 
		final Stripifier st = new Stripifier();
        st.stripify(g);
		
		return g.getGeometryArray();
	}
	
}
